package com.sinosoft.cloud.access.handler;

import com.sinosoft.cloud.access.codec.Decoder;
import com.sinosoft.cloud.access.codec.DecoderFactory;
import com.sinosoft.cloud.access.crypto.CryptoBean;
import com.sinosoft.cloud.access.crypto.CryptoFactory;
import com.sinosoft.cloud.access.limiting.CircuiBreaker;
import com.sinosoft.cloud.access.net.AccessControl;
import com.sinosoft.cloud.access.net.MessageObject;
import com.sinosoft.cloud.access.persist.Persister;
import com.sinosoft.cloud.access.router.Router;
import com.sinosoft.cloud.access.transformer.Trans;
import com.sinosoft.cloud.common.SpringContextUtils;
import com.sinosoft.utility.ExceptionUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.concurrent.ConcurrentHashMap;

import static io.netty.channel.ChannelFutureListener.CLOSE;

/**
 * cloud-netty
 *
 * @title: cloud-netty
 * @package: com.sinosoft.cloud.access.handler
 * @author: yangming
 * @date: 2017/12/2 下午4:05
 */
//@CommonsLog
public abstract class AbstractHandler extends ChannelInboundHandlerAdapter implements Handler {

    private String accessName = null;
    private Integer port = null;
    public static final ConcurrentHashMap<String, String> messageHashTable = new ConcurrentHashMap<>();
    protected final Log logger = LogFactory.getLog(getClass());
    private Long timeStart = null;
    private MessageObject messageObject = null;

    private Trans trans = SpringContextUtils.getBeanByClass(Trans.class);
    private Router router = SpringContextUtils.getBeanByClass(Router.class);
    private CircuiBreaker circuiBreaker = SpringContextUtils.getBeanByClass(CircuiBreaker.class);
    private Persister persister = SpringContextUtils.getBeanByClass(Persister.class);
    private DecoderFactory decoderFactory = DecoderFactory.getInstance();

    AccessControl accessControl = SpringContextUtils.getBeanByClass(AccessControl.class);

    @Override
    public String getAccessName() {
        return accessName;
    }

    @Override
    public void setAccessName(String accessName) {
        this.trans.setAccessName(accessName);
        this.router.setAcceessName(accessName);
        this.circuiBreaker.setAccessName(accessName);
        this.persister.setAccessName(accessName);
        this.accessName = accessName;
    }

    @Override
    public Integer getPort() {
        return port;
    }

    @Override
    public void setPort(Integer port) {
        this.port = port;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            logger.info("进入:" + getClass().getSimpleName() + " thread" + Thread.currentThread().getName());
            if (msg instanceof MessageObject) {
                if (messageHashTable.get(getThreadKey()) == null) {
                    messageHashTable.put(getThreadKey(), getThreadKey());
                }
                //将messageObject缓存
                this.messageObject = (MessageObject) msg;

                MessageObject in = (MessageObject) msg;
//                logger.info("输入参数:" + in.getCurrent());
                MessageObject out = dealMessage(ctx, in);
//                logger.info("返回参数:" + out.getCurrent());
                switch (out.getNext()) {
                    case writeAndFlush:
                        if (out.getSendToBankMsg() != null) {
                            ByteBuf encoded = Unpooled.copiedBuffer(out.getSendToBankMsg().getBytes());
                            ctx.writeAndFlush(encoded).addListener(CLOSE);
                        }
                        break;
                    case fireChannelRead:
                        ctx.fireChannelRead(out);
                        break;
                    case shutDown:
                        break;
                    default:
////                        logger.info("out.getNext() : " + out.getNext());
//                        logger.info(out.toString());
//                        logger.info(getClass() + "无后续动作");
                }
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        super.channelActive(ctx);
        logger.info("连接成功!");
        timeStart = System.currentTimeMillis();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//        super.channelInactive(ctx);
        messageHashTable.remove(getThreadKey());
        logger.info("断开连接!");
        if (timeStart != null) {
            if (null == messageObject.getTransCode() && !messageObject.getHasError()) {
                logger.info("CIP处理总耗时:" + (System.currentTimeMillis() - timeStart));
            } else {
                logger.info("互联网" + messageObject.getTransCode() + "交易总耗时:" + (System.currentTimeMillis() - timeStart));
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        super.exceptionCaught(ctx, cause);
        logger.error(ExceptionUtils.exceptionToString(cause));
        messageObject.setHasError(true);
        String errorStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ABCB2I><Header><RetCode>009999</RetCode><RetMsg>内部错误,交易失败!</RetMsg><SerialNo></SerialNo><InsuSerial/><TransDate></TransDate><TranTime></TranTime><BankCode></BankCode><CorpNo></CorpNo><TransCode></TransCode></Header></ABCB2I>";
        CryptoFactory cryptoFactory = CryptoFactory.getInstance();
        CryptoBean cryptoBean = cryptoFactory.getCryptoBeanByAccess(accessName);

        if (null != cryptoBean) {
            String originalMsg = messageObject.getOriginalMsg().substring(73);
            if (!originalMsg.startsWith("<")) {
                errorStr = cryptoBean.encryt(errorStr);
            }
        }

        Decoder coder = decoderFactory.getDecoder(accessName);
        if (coder != null) {
            errorStr = coder.encoder(errorStr);
        }

        ByteBuf encoded = Unpooled.copiedBuffer(errorStr.getBytes());
        ctx.writeAndFlush(encoded);
        ctx.close();
        messageHashTable.remove(getThreadKey());
    }

    protected String getThreadKey() {
        return Thread.currentThread().getName() + ":" + Thread.currentThread().getId();
    }

    abstract MessageObject dealMessage(ChannelHandlerContext ctx, MessageObject messageObject);

    public Trans getTrans() {
        return trans;
    }

    public Router getRouter() {
        return router;
    }

    public CircuiBreaker getCircuiBreaker() {
        return circuiBreaker;
    }

    public Persister getPersister() {
        return persister;
    }

    public CryptoBean getCryptoBean() {
        CryptoFactory cryptoFactory = CryptoFactory.getInstance();
        CryptoBean cryptoBean = cryptoFactory.getCryptoBeanByAccess(getAccessName());
        return cryptoBean;
    }
}
